//
// Created by jyc on 2021/10/2.
//
#include "imnodes.h"
#include "imgui.h"
#include "RenderGraph.h"
#include "DirectedGraphTraversal.h"
namespace Bonnie{


    std::pair<std::string, std::string> RenderGraph::parseFieldName(const std::string &fullname) {
        if(std::count(fullname.begin(),fullname.end(),'.') == 0){
            return {fullname, nullptr};
        }else{
            auto dot = fullname.find('.');
            return {fullname.substr(0,dot),fullname.substr(dot+1)};
        }
    }

    void RenderGraph::addEdge(const std::string &src, const std::string &dst) {
        auto [srcPassName,srcField] = parseFieldName(src);
        auto [dstPassName,dstField] = parseFieldName(dst);

        Bonnie_Assert(mPassName2NodeIdx.find(srcPassName) != mPassName2NodeIdx.end(),"no such src pass in add edge");
        Bonnie_Assert(mPassName2NodeIdx.find(dstPassName) != mPassName2NodeIdx.end(),"no such dst pass in add edge");
         
        auto edgeIdx = mDGraph.addEdge(mPassName2NodeIdx[srcPassName],mPassName2NodeIdx[dstPassName]);
        mEdgeIdx2EdgeData[edgeIdx] = { src,dst };
    }

    void RenderGraph::RenderUI() {

        static std::vector<std::pair<int, int>> links;

        if(ImGui::Begin("RenderGraph")){

            ImNodes::BeginNodeEditor();
            
            int link_id = 0;
            for(auto nodeInfo:mNodeIdx2NodeData){
                ImNodes::BeginNode(nodeInfo.first);
                ImNodes::BeginNodeTitleBar();
                ImGui::Text(nodeInfo.second.name.c_str());
                ImNodes::EndNodeTitleBar();
                ImGui::Dummy(ImVec2(80.0f, 45.0f));
                
                auto refl = nodeInfo.second.pass->reflect();
                for(int i = 0;i < refl.getFieldCount();i ++){
                    auto field = refl.getField(i);
                    auto fullname = nodeInfo.second.name + "." + field->getName();
                    auto attr_id = mFullname2attrId[fullname];
                    if(field->getVisibility()== RenderPassReflection::Field::Visibility::Output){
                        ImNodes::BeginOutputAttribute(attr_id);
                        ImGui::Text(field->getName().c_str());
                        ImNodes::EndOutputAttribute();
                    }else if (field->getVisibility()== RenderPassReflection::Field::Visibility::Input){
                        ImNodes::BeginInputAttribute(attr_id);
                        ImGui::Text(field->getName().c_str());
                        ImNodes::EndInputAttribute();
                    }
                }
                ImNodes::EndNode();
            }

            for (int i = 0; i < links.size(); ++i)
            {
                const std::pair<int, int> p = links[i];
                ImNodes::Link(i, p.first, p.second);
            }
            ImNodes::EndNodeEditor();
            int start_attr, end_attr;
            if (ImNodes::IsLinkCreated(&start_attr, &end_attr))
            {
                links.push_back(std::make_pair(start_attr, end_attr));
                addEdge(mAttrId2fullname[start_attr],mAttrId2fullname[end_attr]);
            }
        }
        ImGui::End();
    }

    void RenderGraph::compile() {

        mExcutionList.clear();
        mpResourceManager->clearRGResouce();

        auto sortedNodeIds = DirectedGraphTopologicalSort::sort(&mDGraph);

        for (auto id : sortedNodeIds) {
            mExcutionList.push_back({id,&mNodeIdx2NodeData.at(id) });
        }

        // register all bound resource
        for (size_t i = 0; i < mExcutionList.size(); i++)
        {
            const auto& node = mDGraph.getNode(mExcutionList[i].nodeId);
            auto refl = mExcutionList[i].nodeInfo->pass->reflect();

            //register all output resource
            for (size_t j = 0; j < node->getOutcomeEdgeCount(); j++)
            {
                auto srcFullName = mEdgeIdx2EdgeData.at(node->getOutcomeEdge(j)).srcFullName;
                auto [srcPassName, srcField] = parseFieldName(srcFullName);

                for (size_t k = 0; k < refl.getFieldCount(); k++)
                {
                    if (refl.getField(k)->getName() == srcField) {
                        mpResourceManager->registerField(srcFullName, *refl.getField(k), i);
                    }
                }
            }

            // register all input resource
            for (size_t j = 0; j < node->getIncomeEdgeCount(); j++)
            {
                auto dstFullName = mEdgeIdx2EdgeData.at(node->getIncomeEdge(j)).dstFullName;
                auto [dstPassName, dstField] = parseFieldName(dstFullName);

                for (size_t k = 0; k < refl.getFieldCount(); k++)
                {
                    if (refl.getField(k)->getName() == dstField) {
                        mpResourceManager->registerField(dstFullName, *refl.getField(k), i, mEdgeIdx2EdgeData.at(node->getIncomeEdge(j)).srcFullName);
                    }
                }
            }
        }
    }

    void RenderGraph::addPass(const IRenderPass::SharedPtr &pass, const std::string &name) {
        Bonnie_Assert(mPassName2NodeIdx.find(name) == mPassName2NodeIdx.end(),"already exist");
        auto nodeIdx = mDGraph.addNode();
        mPassName2NodeIdx[name] = nodeIdx;

        mNodeIdx2NodeData[nodeIdx] = {name,pass};


        mAttrId2fullname.clear();
        mFullname2attrId.clear();
        int attr_id = 0;
        for (auto nodeInfo : mNodeIdx2NodeData) {
            auto refl = nodeInfo.second.pass->reflect();
            for (int i = 0; i < refl.getFieldCount(); i++) {
                auto field = refl.getField(i);
                auto fullname = nodeInfo.second.name + "." + field->getName();
                mAttrId2fullname.insert({ attr_id,fullname });
                mFullname2attrId.insert({ fullname,attr_id });
                attr_id++;
            }
        }
    }
}
